package birdry.encrypt
{	
	/**
	 * ARIA 암호화 복호화 Class
	 * 작성자 : 박세종 (rhcpn78@gmail.com)
	 */
	public class ARIAUtil
	{
		// property
		private var KRK:Array = [
			[0x517cc1b7, 0x27220a94, 0xfe13abe8, 0xfa9a6ee0],
			[0x6db14acc, 0x9e21c820, 0xff28b1d5, 0xef5de2b0],
			[0xdb92371d, 0x2126e970, 0x03249775, 0x04e8c90e] 
		];
		
		private var S1:Array = new Array(256);
		private var S2:Array = new Array(256);
		private var X1:Array = new Array(256);
		private var X2:Array = new Array(256);
		
		private var TS1:Array = new Array(256);
		private var TS2:Array = new Array(256);
		private var TX1:Array = new Array(256);
		private var TX2:Array = new Array(256);
		
		private var keySize:int=0;
		private var intOfRounds:int=0;
		private var masterKey:Array = new Array();
		private var encRoundKeys:Array = new Array();
		private var decRoundKeys:Array = new Array();
		
		// constructor
		public function ARIAUtil()
		{
			// 데이터 초기화
			init();
		}
		
		// util functions
		private function toByteArray(/*int*/ i:int, /*byte[]*/ b:Array, /*int*/ offset:int):void 
		{
			b[offset  ] = /*(byte)*/(i>>>24);
			b[offset+1] = /*(byte)*/(i>>>16);
			b[offset+2] = /*(byte)*/(i>>> 8);
			b[offset+3] = /*(byte)*/(i     );
		}
		
		private function m(/*int*/ t:int):int 
		{
			return 0x00010101*((t>>>24)&0xff) ^ 0x01000101*((t>>>16)&0xff) ^ 
				0x01010001*((t>>>8)&0xff) ^ 0x01010100*(t&0xff);
		}
		
		private function badc(/*int*/ t:int):int 
		{
			return /*int32*/((t<<8)&0xff00ff00) ^ /*int32*/((t>>>8)&0x00ff00ff);
		}
		
		private function cdab(/*int*/ t:int):int 
		{
			return /*int32*/((t<<16)&0xffff0000) ^ /*int32*/((t>>>16)&0x0000ffff);
		}
		
		private function dcba(/*int*/ t:int):int 
		{
			return /*int32*/((t&0x000000ff)<<24) ^ /*int32*/((t&0x0000ff00)<<8) ^ /*int32*/((t&0x00ff0000)>>>8) ^ /*int32*/((t&0xff000000)>>>24);
		}
		
		private function gsrk(/*int[]*/ x:Array, /*int[]*/ y:Array, /*int*/ rot:int, /*int[]*/ rk:Array, /*int*/ offset:int):void 
		{
			// division error
			var q:int= 4 - ((rot-(rot%32)) / 32), r:int= rot%32, s:int= 32-r; // int
			
			rk[offset]   = /*int32*/(x[0]) ^ /*int32*/(y[(q  )%4]>>>r) ^ /*int32*/(y[(q+3)%4]<<s);
			rk[offset+1] = /*int32*/(x[1]) ^ /*int32*/(y[(q+1)%4]>>>r) ^ /*int32*/(y[(q  )%4]<<s);
			rk[offset+2] = /*int32*/(x[2]) ^ /*int32*/(y[(q+2)%4]>>>r) ^ /*int32*/(y[(q+1)%4]<<s);
			rk[offset+3] = /*int32*/(x[3]) ^ /*int32*/(y[(q+3)%4]>>>r) ^ /*int32*/(y[(q+2)%4]<<s);
		}
		
		private function diff(/*int[]*/ i:Array, /*int*/ offset1:int, /*int[]*/ o:Array, /*int*/ offset2:int):void 
		{
			var t0:int, t1:int, t2:int, t3:int; // int
			
			t0 = 0x00010101*(((i[offset1])>>>24)&0xff) ^ 0x01000101*(((i[offset1])>>>16)&0xff) ^ 0x01010001*(((i[offset1])>>>8)&0xff) ^ 0x01010100*((i[offset1])&0xff);
			t1= 0x00010101*(((i[offset1+1])>>>24)&0xff) ^ 0x01000101*(((i[offset1+1])>>>16)&0xff) ^ 0x01010001*(((i[offset1+1])>>>8)&0xff) ^ 0x01010100*((i[offset1+1])&0xff);
			t2= 0x00010101*(((i[offset1+2])>>>24)&0xff) ^ 0x01000101*(((i[offset1+2])>>>16)&0xff) ^ 0x01010001*(((i[offset1+2])>>>8)&0xff) ^ 0x01010100*((i[offset1+2])&0xff);     
			t3= 0x00010101*(((i[offset1+3])>>>24)&0xff) ^ 0x01000101*(((i[offset1+3])>>>16)&0xff) ^ 0x01010001*(((i[offset1+3])>>>8)&0xff) ^ 0x01010100*((i[offset1+3])&0xff);
			
			t1^=t2; t2^=t3; t0^=t1; t3^=t1; t2^=t0; t1^=t2;
			
			// t1 = badc(t1);
			t1 = (((t1)<<8)&0xff00ff00) ^ (((t1)>>>8)&0x00ff00ff);
			
			// t2 = cdab(t2);
			t2 = (((t2)<<16)&0xffff0000) ^ (((t2)>>>16)&0x0000ffff);
			
			// t3 = dcba(t3);
			t3 = (((t3)&0x000000ff)<<24) ^ (((t3)&0x0000ff00)<<8) ^ (((t3)&0x00ff0000)>>>8) ^ (((t3)&0xff000000)>>>24);
			
			t1^=t2; t2^=t3; t0^=t1; t3^=t1; t2^=t0; t1^=t2;
			o[offset2]=t0; o[offset2+1]=t1; o[offset2+2]=t2; o[offset2+3]=t3;
		}
		
		private function toInt(/*byte*/b0:int, /*byte*/ b1:int,/* byte */b2:int, /*byte*/ b3:int):int {
			return (b0&0xff)<<24 ^ (b1&0xff)<<16 ^ (b2&0xff)<<8 ^ b3&0xff;
		}	
		
		private function swapBlocks(/*int[]*/ arr:Array, /*int*/ offset1:int, /*int*/ offset2:int):void 
		{
			var t:int; // int
			
			for (var i:int=0; i<4; i++) 
			{
				t = arr[offset1+i];
				arr[offset1+i] = arr[offset2+i];
				arr[offset2+i] = t;
			}
		}
		
		private function swapAndDiffuse(/*int[]*/ arr:Array, /*int*/ offset1:int, /*int*/ offset2:int, /*int[]*/ tmp:Array):void 
		{
			diff(arr, offset1, tmp, 0);
			diff(arr, offset2, arr, offset1);
			arr[offset2]=tmp[0]; arr[offset2+1]=tmp[1]; 
			arr[offset2+2]=tmp[2]; arr[offset2+3]=tmp[3];
		}
		
		private function bytes2hex(b:Array):String
		{
			var hex:String = "";
			var HEX_DIGITS:Array = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];
			
			for (var i:int= 0; i < b.length; i++) {
				var a:int= b[i] + 256;
				var a1:int= (a-(a%16))/16;
				
				if (a1 >= 16) { a1 = a1 % 16; }
				
				var a2:int= a % 16;
				
				hex += HEX_DIGITS[a1];
				hex += HEX_DIGITS[a2];
			}
			
			return hex;
		}
		
		private function hex2bytes(h:String):Array
		{
			var HEX_DIGITS:Array = [48,49,50,51,52,53,54,55,56,57,97,98,99,100,101,102];
			var bytes:Array = new Array();
			
			for (var i:int= 0; i < h.length; i++) 
			{
				var a:int= h.charCodeAt(i);
				for (var j:int= 0; j < HEX_DIGITS.length; j++) { if (a == HEX_DIGITS[j]) { a = j; break; } }
				
				var b:int= h.charCodeAt(i+1);
				for (var j1:int= 0; j1 < HEX_DIGITS.length; j1++) { if (b == HEX_DIGITS[j1]) { b = j1; break; } }
				
				bytes[bytes.length] = (a * 16) + b;
				
				i += 1;
			}
			
			return bytes;
		}
		
		private function removeAllArray(value:Array): Array
		{
			if (value == null || value.length == 0) {
				return value;
			}
			
			var length:int = value.length;
			for (var i:int = 0; i < length; i++) {
				delete value[i];
			}
			
			value.splice(0, length);
			
			return value;
		}		
		
		
		// member method
		
		// Static initializer.  For setting up the tables
		private function init(): void
		{
			var exp:Array = new Array(256); // int
			var log:Array = new Array(256); // int
			exp[0] = 1;
			
			for (var i:int= 0; i < 256; i++) { 
				log[i] = 0; 
			}
			
			for (i = 1; i < 256; i++) {
				var j:int= (exp[i-1] << 1) ^ exp[i-1]; // int
				if ((j & 0x100) != 0) j ^= 0x11b;
				exp[i] = j;
			}
			
			for (i=1; i < 255; i++) {
				log[exp[i]] = i;
			}
			
			var A:Array = [  // int
				[1, 0, 0, 0, 1, 1, 1, 1],
				[1, 1, 0, 0, 0, 1, 1, 1],
				[1, 1, 1, 0, 0, 0, 1, 1],
				[1, 1, 1, 1, 0, 0, 0, 1],
				[1, 1, 1, 1, 1, 0, 0, 0],
				[0, 1, 1, 1, 1, 1, 0, 0],
				[0, 0, 1, 1, 1, 1, 1, 0],
				[0, 0, 0, 1, 1, 1, 1, 1]
			];
			var B:Array = [ // int
				[0, 1, 0, 1, 1, 1, 1, 0],
				[0, 0, 1, 1, 1, 1, 0, 1],
				[1, 1, 0, 1, 0, 1, 1, 1],
				[1, 0, 0, 1, 1, 1, 0, 1],
				[0, 0, 1, 0, 1, 1, 0, 0],
				[1, 0, 0, 0, 0, 0, 0, 1],
				[0, 1, 0, 1, 1, 1, 0, 1],
				[1, 1, 0, 1, 0, 0, 1, 1]
			];
			
			for (i=0; i<256; i++) {
				var t:int=0, p:int; // int
				if (i==0) {p=0;}
				else {p=exp[255-log[i]];}
				
				for (j=0; j<8; j++) 
				{
					var s:int=0; // int
					for (var k:int=0; k<8; k++) 
					{
						if (((p >>> (7-k)) & 0x01) != 0) {
							s ^= 0x00FF&(A[k][j]);
						}
					}
					t = /*int32*/((t << 1)) ^ /*int32*/(s);
				}
				
				t ^= 0x63;
				S1[i] = /*(byte)*/0x00FF&(t);
				X1[t] = /*(byte)*/0x00FF&(i);
			}
			
			for (i = 0; i < 256; i++) 
			{
				t = 0, p = 0; // int
				if (i==0) {p=0;}
				else {p=exp[(247*log[i])%255];}
				
				for (j = 0; j < 8; j++) 
				{
					s = 0; // int
					for (k = 0; k < 8; k++) 
					{
						if (((p >>> k) & 0x01) != 0)
							s ^= 0x00FF&(B[7-j][k]);
					}
					t = (t << 1) ^ s;
				}
				
				t^=0xe2;
				S2[i] = /*(byte)*/ 0x00FF&(t);
				X2[t] = /*(byte)*/ 0x00FF&(i);
			}
			
			for (i = 0; i < 256; i++) 
			{
				TS1[i]= 0x00010101 * (S1[i] & 0x0ff);
				TS2[i]= 0x01000101 * (S2[i] & 0x0ff);
				TX1[i]= 0x01010001 * (X1[i] & 0x0ff);
				TX2[i]= 0x01010100 * (X2[i] & 0x0ff);
			}
		}		
		
		
		// initialize value
		private function reset():void 
		{
			this.keySize=0;
			this.intOfRounds=0;
			
			this.removeAllArray(this.masterKey);
			this.masterKey=null;
			
			this.removeAllArray(this.encRoundKeys)
			this.masterKey=null;
			
			this.removeAllArray(this.decRoundKeys)
			this.decRoundKeys=null;
		}
		
		// get Keysize
		private function getKeySize():int
		{
			return this.keySize;
		}
		
		// set Keysize
		private function setKeySize(keySize:int):Boolean
		{
			// 키를 초기화 하면 모든 ARIA에 필요한
			// 데이터를 초기화 함
			this.reset();
			
			if (keySize!=128 && keySize!=192 && keySize!=256) {
				return false;
			}
			
			this.keySize = keySize;
			switch (keySize) {
				case 128:
					this.intOfRounds = 12;
					break;
				case 192:
					this.intOfRounds = 14;
					break;
				case 256:
					this.intOfRounds = 16;
			}
			
			return true;
		}
		
		// set Key
		private function setKey(masterKey:Array):Boolean
		{
			if (masterKey.length*8 < this.keySize) {
				return false;
			}
			
			this.decRoundKeys = null;
			this.encRoundKeys = null;
			
			this.masterKey = new Array();
			for (var i:int= 0; i < masterKey.length; i++) {
				this.masterKey[i] = masterKey[i];
			}
			return true;
		}
		
		// set Encrypt Round Key
		private function setupEncRoundKeys():Boolean 
		{
			if (this.keySize==0 || this.masterKey==null) {
				return false;
			}
			
			if (this.encRoundKeys==null) {
				this.encRoundKeys = new Array(4*(this.intOfRounds+1));
			}
			
			this.decRoundKeys = null;
			this.doEncKeySetup(this.masterKey, this.encRoundKeys, this.keySize);
			
			return true;
		}
		
		// set Decrypt Round Key
		private function setupDecRoundKeys():Boolean
		{
			if (this.keySize==0) {
				return false;
			}
			if (this.encRoundKeys==null) 
			{
				if (this.masterKey==null) {
					return false;
				} else {
					this.setupEncRoundKeys();
				}
			}
			
			this.decRoundKeys = new Array(this.encRoundKeys.length);
			for (var i:int= 0; i < this.encRoundKeys.length; i++) {
				this.decRoundKeys[i] = this.encRoundKeys[i];
			}
			
			this.doDecKeySetup(this.masterKey, this.decRoundKeys, this.keySize);
			
			return true;
		}
		
		private function setupRoundKeys():Boolean {
			return this.setupDecRoundKeys();
		}
		
		/**
		 * 코드 변환
		 * @ param i		암호화 해야 할 byteArray
		 * @ param ioffset	byteArray의 시작위치
		 * @ param rk		roundKey
		 * @ param nr		
		 * @ param o		출력 결과
		 * @ param ooffset	byteArray의 시작위치
		 */
		private function doCrypt(/*byte[]*/ i:Array, ioffset:int, 
											/*int[]*/ rk:Array, nr:int, /*byte[]*/ o:Array, ooffset:int):void
		{
			var t0:int, t1:int, t2:int, t3:int, j:int=0; // int
			
			t0 = (i[ 0+ioffset]&0xff)<<24 ^ (i[ 1+ioffset]&0xff)<<16 ^ (i[ 2+ioffset]&0xff)<<8 ^ i[ 3+ioffset]&0xff;
			t1 = (i[ 4+ioffset]&0xff)<<24 ^ (i[ 5+ioffset]&0xff)<<16 ^ (i[ 6+ioffset]&0xff)<<8 ^ i[ 7+ioffset]&0xff;
			t2 = (i[ 8+ioffset]&0xff)<<24 ^ (i[ 9+ioffset]&0xff)<<16 ^ (i[10+ioffset]&0xff)<<8 ^ i[11+ioffset]&0xff;
			t3 = (i[12+ioffset]&0xff)<<24 ^ (i[13+ioffset]&0xff)<<16 ^ (i[14+ioffset]&0xff)<<8 ^ i[15+ioffset]&0xff;
			
			// float to int
			for (var r:int=1, NN:int=(nr-(nr%2)) / 2; r < NN/*(nr-(nr%2)) / 2*/; r++) 
			{
				t0 ^= /*int32*/(rk[j++]); t1 ^= /*int32*/(rk[j++]); t2 ^= /*int32*/(rk[j++]); t3 ^= /*int32*/(rk[j++]);
				
				t0 = /*int32*/(TS1[0x00FF&(t0>>>24)&0xff]) ^ /*int32*/(TS2[0x00FF&(t0>>>16)&0xff]) ^ /*int32*/(TX1[0x00FF&(t0>>>8)&0xff]) ^ /*int32*/(TX2[0x00FF&(t0)&0xff]);
				t1 = /*int32*/(TS1[0x00FF&(t1>>>24)&0xff]) ^ /*int32*/(TS2[0x00FF&(t1>>>16)&0xff]) ^ /*int32*/(TX1[0x00FF&(t1>>>8)&0xff]) ^ /*int32*/(TX2[0x00FF&(t1)&0xff]);
				t2 = /*int32*/(TS1[0x00FF&(t2>>>24)&0xff]) ^ /*int32*/(TS2[0x00FF&(t2>>>16)&0xff]) ^ /*int32*/(TX1[0x00FF&(t2>>>8)&0xff]) ^ /*int32*/(TX2[0x00FF&(t2)&0xff]);
				t3 = /*int32*/(TS1[0x00FF&(t3>>>24)&0xff]) ^ /*int32*/(TS2[0x00FF&(t3>>>16)&0xff]) ^ /*int32*/(TX1[0x00FF&(t3>>>8)&0xff]) ^ /*int32*/(TX2[0x00FF&(t3)&0xff]);         
				
				t1 ^= /*int32*/(t2); t2 ^= /*int32*/(t3); t0 ^= /*int32*/(t1); t3 ^= /*int32*/(t1); t2 ^= /*int32*/(t0); t1 ^= /*int32*/(t2);
				
				t1 = (((t1)<<8)&0xff00ff00) ^ (((t1)>>>8)&0x00ff00ff);
				t2 = (((t2)<<16)&0xffff0000) ^ (((t2)>>>16)&0x0000ffff);
				t3 = (((t3)&0x000000ff)<<24) ^ (((t3)&0x0000ff00)<<8) ^ (((t3)&0x00ff0000)>>>8) ^ (((t3)&0xff000000)>>>24);
				
				t1 ^= /*int32*/(t2); t2 ^= /*int32*/(t3); t0 ^= /*int32*/(t1); t3 ^= /*int32*/(t1); t2 ^= /*int32*/(t0); t1 ^= /*int32*/(t2);
				t0 ^= /*int32*/(rk[j++]); t1 ^= /*int32*/(rk[j++]); t2 ^= /*int32*/(rk[j++]); t3 ^= /*int32*/(rk[j++]);
				
				t0 = /*int32*/(TX1[0x00FF&(t0>>>24)&0xff]) ^ /*int32*/(TX2[0x00FF&(t0>>>16)&0xff]) ^ /*int32*/(TS1[0x00FF&(t0>>>8)&0xff]) ^ /*int32*/(TS2[0x00FF&(t0)&0xff]);
				t1 = /*int32*/(TX1[0x00FF&(t1>>>24)&0xff]) ^ /*int32*/(TX2[0x00FF&(t1>>>16)&0xff]) ^ /*int32*/(TS1[0x00FF&(t1>>>8)&0xff]) ^ /*int32*/(TS2[0x00FF&(t1)&0xff]);
				t2 = /*int32*/(TX1[0x00FF&(t2>>>24)&0xff]) ^ /*int32*/(TX2[0x00FF&(t2>>>16)&0xff]) ^ /*int32*/(TS1[0x00FF&(t2>>>8)&0xff]) ^ /*int32*/(TS2[0x00FF&(t2)&0xff]);
				t3 = /*int32*/(TX1[0x00FF&(t3>>>24)&0xff]) ^ /*int32*/(TX2[0x00FF&(t3>>>16)&0xff]) ^ /*int32*/(TS1[0x00FF&(t3>>>8)&0xff]) ^ /*int32*/(TS2[0x00FF&(t3)&0xff]);
				
				t1 ^= /*int32*/(t2); t2^=/*int32*/(t3); t0^=/*int32*/(t1); t3^=/*int32*/(t1); t2^=/*int32*/(t0); t1^=/*int32*/(t2);
				
				t3 = (((t3)<<8)&0xff00ff00) ^ (((t3)>>>8)&0x00ff00ff);
				
				t0 = (((t0)<<16)&0xffff0000) ^ (((t0)>>>16)&0x0000ffff);
				t1 = (((t1)&0x000000ff)<<24) ^ (((t1)&0x0000ff00)<<8) ^ (((t1)&0x00ff0000)>>>8) ^ (((t1)&0xff000000)>>>24);
				t1 ^= /*int32*/(t2); t2^=/*int32*/(t3); t0^=/*int32*/(t1); t3^=/*int32*/(t1); t2^=/*int32*/(t0); t1^=/*int32*/(t2);
			}
			
			
			t0^=/*int32*/(rk[j++]); t1^=/*int32*/(rk[j++]); t2^=/*int32*/(rk[j++]); t3^=/*int32*/(rk[j++]);
			t0=/*int32*/(TS1[(t0>>>24)&0xff])^/*int32*/(TS2[(t0>>>16)&0xff])^/*int32*/(TX1[(t0>>>8)&0xff])^/*int32*/(TX2[t0&0xff]);
			t1=/*int32*/(TS1[(t1>>>24)&0xff])^/*int32*/(TS2[(t1>>>16)&0xff])^/*int32*/(TX1[(t1>>>8)&0xff])^/*int32*/(TX2[t1&0xff]);
			t2=/*int32*/(TS1[(t2>>>24)&0xff])^/*int32*/(TS2[(t2>>>16)&0xff])^/*int32*/(TX1[(t2>>>8)&0xff])^/*int32*/(TX2[t2&0xff]);
			t3=/*int32*/(TS1[(t3>>>24)&0xff])^/*int32*/(TS2[(t3>>>16)&0xff])^/*int32*/(TX1[(t3>>>8)&0xff])^/*int32*/(TX2[t3&0xff]);
			
			t1^=/*int32*/(t2); t2^=/*int32*/(t3); t0^=/*int32*/(t1); t3^=/*int32*/(t1); t2^=/*int32*/(t0); t1^=/*int32*/(t2);
			t1 = (((t1)<<8)&0xff00ff00) ^ (((t1)>>>8)&0x00ff00ff);
			t2 = (((t2)<<16)&0xffff0000) ^ (((t2)>>>16)&0x0000ffff);
			t3 = (((t3)&0x000000ff)<<24) ^ (((t3)&0x0000ff00)<<8) ^ (((t3)&0x00ff0000)>>>8) ^ (((t3)&0xff000000)>>>24);
			
			t1 ^= /*int32*/(t2); t2 ^= /*int32*/(t3); t0^=/*int32*/(t1); t3^=/*int32*/(t1); t2^=/*int32*/(t0); t1^=/*int32*/(t2);
			t0 ^= /*int32*/(rk[j++]); t1^=/*int32*/(rk[j++]); t2^=/*int32*/(rk[j++]); t3^=/*int32*/(rk[j++]); 
			
			o[ 0+ooffset] = /*(byte)*/(0x00FF&(X1[0xff&(t0>>>24)]) ^ 0x00FF&((rk[j  ]>>>24)));
			o[ 1+ooffset] = /*(byte)*/(0x00FF&(X2[0xff&(t0>>>16)]) ^ 0x00FF&((rk[j  ]>>>16)));
			o[ 2+ooffset] = /*(byte)*/(0x00FF&(S1[0xff&(t0>>> 8)]) ^ 0x00FF&((rk[j  ]>>> 8)));
			o[ 3+ooffset] = /*(byte)*/(0x00FF&(S2[0xff&(t0     )]) ^ 0x00FF&((rk[j  ]     )));
			o[ 4+ooffset] = /*(byte)*/(0x00FF&(X1[0xff&(t1>>>24)]) ^ 0x00FF&((rk[j+1]>>>24)));
			o[ 5+ooffset] = /*(byte)*/(0x00FF&(X2[0xff&(t1>>>16)]) ^ 0x00FF&((rk[j+1]>>>16)));
			o[ 6+ooffset] = /*(byte)*/(0x00FF&(S1[0xff&(t1>>> 8)]) ^ 0x00FF&((rk[j+1]>>> 8)));
			o[ 7+ooffset] = /*(byte)*/(0x00FF&(S2[0xff&(t1     )]) ^ 0x00FF&((rk[j+1]     )));
			o[ 8+ooffset] = /*(byte)*/(0x00FF&(X1[0xff&(t2>>>24)]) ^ 0x00FF&((rk[j+2]>>>24)));
			o[ 9+ooffset] = /*(byte)*/(0x00FF&(X2[0xff&(t2>>>16)]) ^ 0x00FF&((rk[j+2]>>>16)));
			o[10+ooffset] = /*(byte)*/(0x00FF&(S1[0xff&(t2>>> 8)]) ^ 0x00FF&((rk[j+2]>>> 8)));
			o[11+ooffset] = /*(byte)*/(0x00FF&(S2[0xff&(t2     )]) ^ 0x00FF&((rk[j+2]     )));
			o[12+ooffset] = /*(byte)*/(0x00FF&(X1[0xff&(t3>>>24)]) ^ 0x00FF&((rk[j+3]>>>24)));
			o[13+ooffset] = /*(byte)*/(0x00FF&(X2[0xff&(t3>>>16)]) ^ 0x00FF&((rk[j+3]>>>16)));
			o[14+ooffset] = /*(byte)*/(0x00FF&(S1[0xff&(t3>>> 8)]) ^ 0x00FF&((rk[j+3]>>> 8)));
			o[15+ooffset] = /*(byte)*/(0x00FF&(S2[0xff&(t3     )]) ^ 0x00FF&((rk[j+3]     )));
		}
		
		/**
		 * 암호화를 요청
		 * @ param i		암호화 해야 할 byteArray
		 * @ param ioffset	byteArray의 시작위치	
		 * @ param o		출력 결과
		 * @ param ooffset	byteArray의 시작위치
		 */
		private function encrypt(/*byte[]*/ i:Array, /*int*/ ioffset:int, /*byte[]*/ o:Array, /*int*/ ooffset:int):Boolean 
		{
			if (this.keySize==0) { 
				return false; 
			}
			
			if (this.encRoundKeys == null) {
				if (this.masterKey == null) {
					return false
				} else {
					this.setupEncRoundKeys();
				}
			}
			
			doCrypt(i, ioffset, this.encRoundKeys, this.intOfRounds, o, ooffset);
			
			return true;
		}
		
		/**
		 * 복호화를 요청
		 * @ param i		복호화 해야 할 byteArray
		 * @ param ioffset	byteArray의 시작위치	
		 * @ param o		출력 결과
		 * @ param ooffset	byteArray의 시작위치
		 */		
		private function decrypt(/*byte[]*/ i:Array, /*int*/ ioffset:int, /*byte[]*/ o:Array, /*int*/ ooffset:int):Boolean
		{
			if (this.keySize==0) {
				return false;
			}
			
			if (this.decRoundKeys == null) {
				if (this.masterKey == null) {
					return false;
				} else {
					this.setupDecRoundKeys();
				}
			}
			
			doCrypt(i, ioffset, this.decRoundKeys, this.intOfRounds, o, ooffset);
			
			return true;
		}
		
		/**
		 * 암호화 키 설정
		 */
		private function doEncKeySetup(/*byte[]*/ mk:Array, /*int[]*/ rk:Array, /*int*/ keyBits:int):void 
		{      
			var t0:int, t1:int, t2:int, t3:int, q:int, j:int=0;
			var w0:Array = new Array(4);
			var w1:Array = new Array(4);
			var w2:Array = new Array(4);
			var w3:Array = new Array(4);
			
			w0[0] = toInt(mk[ 0], mk[ 1], mk[ 2], mk[ 3]);
			w0[1] = toInt(mk[ 4], mk[ 5], mk[ 6], mk[ 7]);
			w0[2] = toInt(mk[ 8], mk[ 9], mk[10], mk[11]);
			w0[3] = toInt(mk[12], mk[13], mk[14], mk[15]);
			
			// w0[0] = toInt(mk[ 0], mk[ 1], mk[ 2], mk[ 3]);
			w0[0] = ((mk[ 0])&0xff)<<24 ^ ((mk[ 1])&0xff)<<16 ^ ((mk[ 2])&0xff)<<8 ^ (mk[ 3])&0xff;
			
			// w0[1] = toInt(mk[ 4], mk[ 5], mk[ 6], mk[ 7]);
			w0[1] = ((mk[ 4])&0xff)<<24 ^ ((mk[ 5])&0xff)<<16 ^ ((mk[ 6])&0xff)<<8 ^ (mk[ 7])&0xff;
			
			// w0[2] = toInt(mk[ 8], mk[ 9], mk[10], mk[11]);
			w0[2] = ((mk[ 8])&0xff)<<24 ^ ((mk[ 9])&0xff)<<16 ^ ((mk[10])&0xff)<<8 ^ (mk[11])&0xff;
			
			// w0[3] = toInt(mk[12], mk[13], mk[14], mk[15]);
			w0[3] = ((mk[12])&0xff)<<24 ^ ((mk[13])&0xff)<<16 ^ ((mk[14])&0xff)<<8 ^ (mk[15])&0xff;
			
			// division error
			q = ((keyBits - 128) - ((keyBits - 128)%64)) / 64;
			t0=w0[0]^KRK[q][0]; t1=w0[1]^KRK[q][1];
			t2=w0[2]^KRK[q][2]; t3=w0[3]^KRK[q][3];  
			t0=TS1[(t0>>>24)&0xff]^TS2[(t0>>>16)&0xff]^TX1[(t0>>>8)&0xff]^TX2[t0&0xff];
			t1=TS1[(t1>>>24)&0xff]^TS2[(t1>>>16)&0xff]^TX1[(t1>>>8)&0xff]^TX2[t1&0xff];
			t2=TS1[(t2>>>24)&0xff]^TS2[(t2>>>16)&0xff]^TX1[(t2>>>8)&0xff]^TX2[t2&0xff];
			t3=TS1[(t3>>>24)&0xff]^TS2[(t3>>>16)&0xff]^TX1[(t3>>>8)&0xff]^TX2[t3&0xff];   
			
			t1^=t2; t2^=t3; t0^=t1; t3^=t1; t2^=t0; t1^=t2;
			
			t1 = (((t1)<<8)&0xff00ff00) ^ (((t1)>>>8)&0x00ff00ff);
			t2 = (((t2)<<16)&0xffff0000) ^ (((t2)>>>16)&0x0000ffff);
			t3 = (((t3)&0x000000ff)<<24) ^ (((t3)&0x0000ff00)<<8) ^ (((t3)&0x00ff0000)>>>8) ^ (((t3)&0xff000000)>>>24);
			
			t1^=t2; t2^=t3; t0^=t1; t3^=t1; t2^=t0; t1^=t2;
			
			if (keyBits > 128) {
				w1[0] = toInt(mk[16], mk[17], mk[18], mk[19]);
				w1[1] = toInt(mk[20], mk[21], mk[22], mk[23]);
				if (keyBits > 192) {
					w1[2] = toInt(mk[24], mk[25], mk[26], mk[27]);
					w1[3] = toInt(mk[28], mk[29], mk[30], mk[31]);
				} else {
					w1[2]=w1[3]=0;
				}
			} else {
				w1[0]=w1[1]=w1[2]=w1[3]=0;
			}
			w1[0]^=t0; w1[1]^=t1; w1[2]^=t2; w1[3]^=t3;
			t0=w1[0];  t1=w1[1];  t2=w1[2];  t3=w1[3];
			
			q = (q==2)? 0 : (q+1);
			t0^=KRK[q][0]; t1^=KRK[q][1]; t2^=KRK[q][2]; t3^=KRK[q][3];
			t0=TX1[(t0>>>24)&0xff]^TX2[(t0>>>16)&0xff]^TS1[(t0>>>8)&0xff]^TS2[t0&0xff];
			t1=TX1[(t1>>>24)&0xff]^TX2[(t1>>>16)&0xff]^TS1[(t1>>>8)&0xff]^TS2[t1&0xff];
			t2=TX1[(t2>>>24)&0xff]^TX2[(t2>>>16)&0xff]^TS1[(t2>>>8)&0xff]^TS2[t2&0xff];
			t3=TX1[(t3>>>24)&0xff]^TX2[(t3>>>16)&0xff]^TS1[(t3>>>8)&0xff]^TS2[t3&0xff]; 
			t1^=t2; t2^=t3; t0^=t1; t3^=t1; t2^=t0; t1^=t2;
			
			t3 = (((t3)<<8)&0xff00ff00) ^ (((t3)>>>8)&0x00ff00ff);
			t0 = (((t0)<<16)&0xffff0000) ^ (((t0)>>>16)&0x0000ffff);      
			t1 = (((t1)&0x000000ff)<<24) ^ (((t1)&0x0000ff00)<<8) ^ (((t1)&0x00ff0000)>>>8) ^ (((t1)&0xff000000)>>>24);
			
			t1^=t2; t2^=t3; t0^=t1; t3^=t1; t2^=t0; t1^=t2;
			t0^=w0[0]; t1^=w0[1]; t2^=w0[2]; t3^=w0[3];
			w2[0]=t0; w2[1]=t1; w2[2]=t2; w2[3]=t3;
			
			q = (q==2)? 0 : (q+1);
			t0^=KRK[q][0]; t1^=KRK[q][1]; t2^=KRK[q][2]; t3^=KRK[q][3];
			t0=TS1[(t0>>>24)&0xff]^TS2[(t0>>>16)&0xff]^TX1[(t0>>>8)&0xff]^TX2[t0&0xff];
			t1=TS1[(t1>>>24)&0xff]^TS2[(t1>>>16)&0xff]^TX1[(t1>>>8)&0xff]^TX2[t1&0xff];
			t2=TS1[(t2>>>24)&0xff]^TS2[(t2>>>16)&0xff]^TX1[(t2>>>8)&0xff]^TX2[t2&0xff];
			t3=TS1[(t3>>>24)&0xff]^TS2[(t3>>>16)&0xff]^TX1[(t3>>>8)&0xff]^TX2[t3&0xff];   
			t1^=t2; t2^=t3; t0^=t1; t3^=t1; t2^=t0; t1^=t2;
			
			t1 = (((t1)<<8)&0xff00ff00) ^ (((t1)>>>8)&0x00ff00ff);
			t2 = (((t2)<<16)&0xffff0000) ^ (((t2)>>>16)&0x0000ffff);
			t3 = (((t3)&0x000000ff)<<24) ^ (((t3)&0x0000ff00)<<8) ^ (((t3)&0x00ff0000)>>>8) ^ (((t3)&0xff000000)>>>24);
			
			t1^=t2; t2^=t3; t0^=t1; t3^=t1; t2^=t0; t1^=t2;
			w3[0]=t0^w1[0]; w3[1]=t1^w1[1]; w3[2]=t2^w1[2]; w3[3]=t3^w1[3];
			
			gsrk(w0, w1, 19, rk, j); j+=4;
			gsrk(w1, w2, 19, rk, j); j+=4;
			gsrk(w2, w3, 19, rk, j); j+=4;
			gsrk(w3, w0, 19, rk, j); j+=4;
			gsrk(w0, w1, 31, rk, j); j+=4;
			gsrk(w1, w2, 31, rk, j); j+=4;
			gsrk(w2, w3, 31, rk, j); j+=4;
			gsrk(w3, w0, 31, rk, j); j+=4;
			gsrk(w0, w1, 67, rk, j); j+=4;
			gsrk(w1, w2, 67, rk, j); j+=4;
			gsrk(w2, w3, 67, rk, j); j+=4;
			gsrk(w3, w0, 67, rk, j); j+=4;
			gsrk(w0, w1, 97, rk, j); j+=4;
			
			if (keyBits > 128) {  
				gsrk(w1, w2, 97, rk, j); j+=4;
				gsrk(w2, w3, 97, rk, j); j+=4;
			}
			if (keyBits > 192) {
				gsrk(w3, w0,  97, rk, j); j+=4;
				gsrk(w0, w1, 109, rk, j);
			}
		}
		
		/**
		 * 복호화 키를 설정
		 */
		private function doDecKeySetup(mk:Array, rk:Array, keyBits:int):void 
		{
			var a:int=0, z:int;
			var t:Array = new Array(4);
			
			z=32+keyBits/8;
			swapBlocks(rk, 0, z);
			a+=4; z-=4;
			
			for (; a<z; a+=4, z-=4)
				swapAndDiffuse(rk, a, z, t);
			diff(rk, a, t, 0);
			rk[a]=t[0]; rk[a+1]=t[1]; rk[a+2]=t[2]; rk[a+3]=t[3];
		}
		
		/** 
		 * ARIA 암호화를 요청 한다
		 * @ param input		암호화 하고자 하는 문자열
		 * @ param password		비밀번호
		 * @ param keyBits		키 설정 
		 */
		public function getEncrypt(input:String, password:String, keyBits:int = 256):String
		{
			// 적합성 체크
			if (input == null || input == "" || password == null || password == "") {
				return null;
			}
			
			// 키를 지정
			this.setKeySize(keyBits);
			
			// masterKey 생성
			var mk:Array = new Array(32);
			for (var i:int= 0; i < 16; i++) 
			{
				if (i >= password.length) break;
				
				// 하나의 단어가 2바이트 
				var code:int= password.charCodeAt(i);
				mk[i*2 + 0] = (code - (code%256))/256;
				mk[i*2 + 1] = code % 256;
			}
			
			this.setKey(mk);
			this.setupRoundKeys();
			this.removeAllArray(mk);			
			
			// 암호화 버퍼
			var oobuf:Array = new Array();
			
			// 암호화 한 문자 길이
			var sizeOfWord:int= 0;
			
			// 문장을 탐색하면서 16바이트 단위로
			// 쪼개가며 암호화 한다.
			// 쪼개진 문장은 하나로 합쳐서 hex 문자열로 변환 한다.
			var inputLen:int = input.length;
			while (sizeOfWord < inputLen) 
			{
				// 임시 버퍼
				var cur_buf:Array = new Array(16);
				
				// 하나의 단어가 2바이트 
				for (var i2:int= 0; i2 < 8; i2++) 
				{
					if ((sizeOfWord + i2) >= input.length) break;
					
					var code1:int= input.charCodeAt(sizeOfWord+i2);
					cur_buf[i2*2+0] = (code1 - (code1%256))/256;
					cur_buf[i2*2+1] = code1 % 256;
				}
				
				// 16바이트씩 배열 생성
				for (var i3:int = 0; i3 < 16; i3++) { oobuf[oobuf.length] = 0; }
				
				// 암호화 시작
				this.encrypt(cur_buf, 0, oobuf, oobuf.length-16);
				
				// 다음 진행
				sizeOfWord += 8;
				removeAllArray(cur_buf);
			}	
			
			// 16 진수로 바꿈
			var hex:String = bytes2hex(oobuf);
			
			// 삭제
			removeAllArray(oobuf);
			
			return hex;
		}
		
		/**
		 * 복호화를 요청 한다
		 * @ param hex			암호화된 문자열
		 * @ param password		비밀번호
		 * @ param keyBit		키 설정
		 */
		public function getDecrypt(hex:String, password:String, keyBits:int = 256):String
		{
			// 적합성 체크
			if (hex == null || hex == "") {
				return null;
			}
			
			// 키를 지정
			this.setKeySize(keyBits);
			
			// masterKey 생성
			var mk:Array = new Array(32);
			for (var i:int= 0; i < 16; i++) 
			{
				if (i >= password.length) break;
				
				// 하나의 단어가 2바이트 
				var code:int= password.charCodeAt(i);
				mk[i*2 + 0] = (code - (code%256))/256;
				mk[i*2 + 1] = code % 256;
			}
			
			this.setKey(mk);
			this.setupRoundKeys();
			this.removeAllArray(mk);
			
			// 일단 암호화된 문자열을 byte로 변환 한다.
			var oobuf:Array = hex2bytes(hex);
			
			// 결과값 버퍼
			var oibuf:Array = new Array();
			
			// 복호화 한 문자열 길이
			var sizeOfWord:int= 0;
						
			// 16바이트 단위로 암호화된 문장을
			// 쪼개가면서 복호화 한다.
			// 복호화된 각각의 결과는 버퍼에 저장하여
			// 하나로 합한다.
			var hLen:int = oobuf.length;
			while (sizeOfWord < hLen) 
			{
				// 임시 버퍼
				var cur_buf:Array = new Array(16);

				// 암호화 된 구문은 16바이트 단위로 이루어짐
				for (var ii:int= 0; ii < 16; ii++) {
					if ((sizeOfWord + ii) >= oobuf.length) { break; }
					cur_buf[ii] = oobuf[sizeOfWord+ii];
				}
				
				// 추출한 16바이트의 문장을 복호화 한다.
				for (var q:int= 0; q < 16; q++) { oibuf[oibuf.length] = 0; }
				
				// 복호화 시작
				decrypt(cur_buf, 0, oibuf, oibuf.length-16);
				
				// 진행
				sizeOfWord += 16;
				removeAllArray(cur_buf);
			}
			
			removeAllArray(oobuf);
			
			// 복호화 한 문자열을 다시 원래 문자열로 되돌린다.
			var text:String = "";
			for (var i3:int= 0; i3 < oibuf.length; i3++) 
			{
				// 2바이트니까 2바이트씩 ..
				oibuf[i3] = (oibuf[i3]+256) % 256;
				oibuf[i3+1] = (oibuf[i3+1]+256) % 256;
				
				var code1:int= (oibuf[i3]*256)+oibuf[i3+1];
				text += String.fromCharCode(code1);
				
				i3 += 1;
			}
			
			removeAllArray(oibuf);

			return text;
		}
	}
}